package com.whz.generic.uuid;

import com.github.f4b6a3.uuid.UuidCreator;
import lombok.experimental.UtilityClass;

import java.math.BigInteger;
import java.util.UUID;

/**
 * 基于时间戳和机器IP的UUID生成器,Java的uuid最大问题是基于安全随机数,高并发时随机数不足会阻塞线程
 * <p>
 * 解决办法是可以配置
 * 1) -Djava.security.egd=file:/dev/./urandom 无阻赛随机数文件,SHA1PRNG算法
 * 2) -Djava.security.egd=file:/dev/urandom 无阻赛随机数文件,nativePRNG算法
 * 3) -Djava.security.egd=file:/dev/random 阻赛随机数文件,SHA1PRNG算法
 *
 * @author whz
 */
@UtilityClass
public final class UuidUtils {

    private static final String ALPHABET = "abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789";
    private static final String SPEC_ALPHABET = ALPHABET + "!@#$%^&*";
    private static final char[] BASE_64 = ALPHABET.toCharArray();
    private static final char[] SPEC_ = SPEC_ALPHABET.toCharArray();
    public static final int INT10 = 10;
    public static final int INT6 = 6;

    /**
     * 传建一个UUID的正数
     *
     * @return 表示uuid的BigInteger
     */
    public static BigInteger createNumberUid() {
        UUID uuid = createUuid();
        BigInteger value1 = BigInteger.valueOf(uuid.getMostSignificantBits());
        BigInteger value2 = BigInteger.valueOf(uuid.getLeastSignificantBits());
        if (value1.compareTo(value2) < 0) {
            return value2.multiply(value2).add(value1);
        }
        return value1.multiply(value1).add(value1).add(value2);
    }

    private static UUID createUuid() {
        // 基于时间戳和机器名(IP)而不是安全随机数
        return UuidCreator.getTimeBased();
    }


    /**
     * 压缩的uuid
     *
     * @return 22 byte 的uuid
     */
    public static String generateUid() {
        UUID uuid = createUuid();
        char[] chs = new char[22];
        long most = uuid.getMostSignificantBits();
        long least = uuid.getLeastSignificantBits();
        int high = (int) (most >> 13 ^ least >> 31) & 0x3c;
        int k = chs.length - 1;
        for (int i = 0; i < INT10; i++, least >>>= INT6) {
            chs[k--] = BASE_64[(int) (least & 0x3f)];
        }
        chs[k--] = BASE_64[(int) (least & 0x3f | most & 0x30)];
        most >>>= 2;
        for (int i = 0; i < INT10; i++, most >>>= INT6) {
            chs[k--] = BASE_64[(int) (most & 0x3f)];
        }
        chs[k--] = BASE_64[(int) (high | most)];
        return new String(chs);
    }
}